Kealdish's Studio.

iOS 10推送通知小记(上)

字数统计: 1.6k阅读时长: 6 min
2016/07/23 Share

概览

最近在体验iOS 10Beta版的过程中,我发现iOS 10在锁屏界面的变动比较大,与之前的交互逻辑完全不同。与此同时,推送通知消息也有了改进,比如支持3D Touch,可以展示更多的信息,通知内容可以包含图片等等。按照逻辑推断iOS 10推送通知的API会有变化,于是,我赶紧查阅了Xcode 8Beta中的文档以及WWDC2016的视频,果然发现了玄机。

推送机制

推送机制在iOS 10上发生了一点变化。

iOS < 10.0

推送按照通知发送源分为本地推送和远程推送。

先说本地推送。本地推送是由设备上的app发起的,本地推送通知被加入到schedule中后,被诸如时间、位置、任务等触发通知而使其显示到设备上。当用户对通知消息做出反应,发起通知的app会得到回调。

再说远程推送。远程推送是由app的服务器通过网络连接发送的。app先注册远程通知得到APNS(Apple Push Notification Service)返回的device token,app的服务端将推送通知的负载连同device token发送给APNS。APNS根据device token将推送通知发送到用户的设备上。设备接收到通知后会展示在屏幕上供用户浏览和处理,之后app会得到用户处理的回调。

iOS >= 10.0

iOS 10新增了trigger。依据字面意思理解就是推送通知的触发器,告诉设备什么时候触发通知显示在设备上。iOS 10支持四种类型的trigger:push、timer interval、calendar and location。其中push属于远程推送,其他三种属于本地推送。

iOS 10推送通知的过程变成了这样:app注册通知,创建通知的内容,trigger,identifier,将这些放入UNNotificationRequest对象中,最后将其放入schedule。

Notification Service Extensions

在iOS 10之前,推送通知的负载大小最多也就4KB,因而类似于声音、图像、视频等无法存储到推送通知当中。为了解决这个问题,iOS 10引入了新的扩展服务——Notification Service ExtensionsNotification Service Extensions可以帮助我们在不打开app的情况下去下载或者是改变远程推送通知消息中的内容。由于推送通知的负载不足以存储多媒体文件本身,因而以URL的形式存储。一旦设备接收到来自于你的app的远程推送通知的负载,Notification Service Extensions便去下载URL当中的资源文件,然后会在用户查看通知消息时展现给用户。

那么它是如何工作的呢?在通知的服务端,发送的通知会被service extension加工成两个部分:

1
2
3
4
5
6
7
{
aps: {
alert: {...}
mutable-content: 1
}
my-attachment: "https://foo.bar/baz.jpg"
}

mutable-content字段告诉iOS在通知被发送之前通知中有内容要被service extension修改,my-attachment字段包含了被下载内容的URL。

为了能让app的扩展服务能够处理通知消息,我们需要在app的Xcode project中添加notification service extension。Xcode会生成一个包含UNNotificationServiceExtension子类的文件。

1
2
3
4
5
6
7
8
9
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler:(UNNotificationContent) -> Void) {
// ...
}
// ...
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
// ...
}

这个文件包含两个需要被重写的方法。第一个方法用来下载URL中的内容并将其添加到通知当中。由于网络是不可靠的,因此我们还需要在此方法中处理网络发生错误的情况。第二个方法是用来处理第一个方法运行超时的情况,当attachment下载消耗太长的时间时,操作系统会执行serviceExtensionTimeWillExpire()方法终止下载并发送类似于”best attempt”内容的通知。

Media Attachments

Media Attachments是用来附着在推送通知上负责存储多媒体文件的URL。目前支持的多媒体文件类型包括常用的音频、视频和图片(包括GIF)格式。

1
2
3
4
5
6
7
8
9
10
11
// Adding an attachment to a user notification
public class NotificationService: UNNotificationServiceExtension {
override public func didReceive(_ request: UNNotificationRequest,withContentHandler contentHandler: (UNNotificationContent) -> Void) {
let fileURL = // ...
let attachment = UNNotificationAttachment(identifier: "image",url: fileURL,
options: nil)
let content = request.content.mutableCopy as! UNMutableNotificationContent
content.attachments = [ attachment ]
contentHandler(content)
}
}

Media Attachments在本地推送与远程推送的工作机制有轻微差别。在本地推送中,它的media attachment在app创建通知时就必须包含某个本地磁盘文件的URL。当通知生效时,那份文件会被拷贝一份随着通知一起发送出去。除此之外,不需要做别的工作。在远程推送中,远程通知服务会将media attachment作为APNS通知负载的一部分发送出去。media attachment中包含的URL的内容不限定必须是已经存在设备中的文件。然而,iOS 10无法自动发送attachment内容不存储在本地的通知,这就要借助到前面提到的Notification Service Extensions

Notification Content Extensions

如果我们想在通知中展现app中一样的界面内容时,该怎么办?Notification Content Extensions派上用场了。Notification Content ExtensionsNotification Service Extensions在通知中提供新的内容方面有相似之处,但两者有两个重要的区别。第一,Notification Content Extensions在本地推送和远程推送当中都起作用,而Notification Service Extensions只在远程推送中工作。第二,Notification Content Extensions只能处理media attachment中的内容,而Notification Service Extensions不仅能处理media attachment中的内容,更能在通知详情中展示整个的UIViewController

在上图中我们看到了日历通知的日历事件就是用到了Notification Content Extensions。在Xcode项目中添加Notification Content ExtensionsNotification Service Extensions的操作类似。

1
2
3
4
5
6
7
8
9
10
11
12
13
// Minimal Content Extension
class NotificationViewController: UIViewController, UNNotificationContentExtension {
@IBOutlet var label: UILabel?
override func viewDidLoad() {
super.viewDidLoad()
// Do any required interface initialization here.
}
func didReceive(_ notification: UNNotification) {
label?.text = notification.request.content.body
}
}

创建成功后,Xcode会生成一个storyboard文件、代码文件以及可以自定义contentn extension内容的info.plist文件。代码文件中包含了一个实现UNNotificationContentExtension协议的UIViewController的子类。要想让它生效,我们需要实现协议中必须实现的方法,在storyboard中自定义view设置info.plist文件中的category ID。

结语

原理部分的内容就这么多了,关于推送的UserNotifications.framework代码方面的内容会放到下一篇中。

CATALOG
  1. 1. 概览
  2. 2. 推送机制
    1. 2.1. iOS < 10.0
    2. 2.2. iOS >= 10.0
  3. 3. Notification Service Extensions
  4. 4. Media Attachments
  5. 5. Notification Content Extensions
  6. 6. 结语